package master

import (
	"GoCrontab/crontab/common"
	"encoding/json"
	"fmt"
	"net/http"
	//"path/filepath"
	"strconv"
	"time"
)

//提供http 服务端，供任务的http接口
type ApiServer struct {
	httpServer *http.Server
}

var (
	//singleton singleton ，默认是空指针，注意首字母大写，供其他模块使用
	GlobalApiServer *ApiServer
)

//init http service
func InitApiServer() (err error) {

	var (
		serveMux *http.ServeMux
		//listener   net.Listener
		httpServer *http.Server

		httpStaticDir http.Dir
		staticHandler http.Handler
	)
	//配置路由
	serveMux = http.NewServeMux()
	serveMux.HandleFunc("/job/save", handleJobSave)
	//测试输出
	serveMux.HandleFunc("/job/view", handleJobView)
	//删除
	serveMux.HandleFunc("/job/del", handleJobDelete)
	serveMux.HandleFunc("/job/list", handleJobList)
	serveMux.HandleFunc("/job/kill", handleJobKill)
	serveMux.HandleFunc("/worker/list", handleWorkerList)
	serveMux.HandleFunc("/job/logs", handleJobLog)

	//静态文件目录配置及路由配置
	dir := GlobalConfig.WebStaticDirTwoT460
	httpStaticDir = http.Dir(dir) //执行根目录是master.go
	staticHandler = http.FileServer(httpStaticDir)
	serveMux.Handle("/", http.StripPrefix("/", staticHandler))
	//启动tcp监听接口
	//if listener, err = net.Listen("tcp", "0.0.0.0:8880"); err != nil {
	//	//错误抛出给调用方
	//	return
	//}
	// 启动http服务
	httpServer = &http.Server{
		//收到请求会会调用handler方法，与serveMux HandleFunc一致，匹配到路由后再次转发，类似代理模式
		//Addr:              ":" + strconv.Itoa(GlobalConfig.ApiPort),
		Addr:              ":8070",
		Handler:           serveMux,
		ReadTimeout:       time.Duration(GlobalConfig.ApiReadTimeOut) * time.Millisecond,
		WriteTimeout:      time.Duration(GlobalConfig.ApiWriteTimeOut) * time.Millisecond,
		ReadHeaderTimeout: 0,
		//ErrorLog:*log.Fatalf
	}
	GlobalApiServer = &ApiServer{httpServer: httpServer}
	//启动一个协程，启动http服务，测试的时候注意：如果主函数退出，那他肯定也退出了。
	//go httpServer.Serve(listener)
	go httpServer.ListenAndServe()
	//go log.Fatal(httpServer.ListenAndServe())
	fmt.Println("http server success")

	return nil

}

//查询任务日志，需要支持分页@todo
func handleJobLog(writer http.ResponseWriter, request *http.Request) {

	var (
		err        error
		jobName    string
		skipParam  string
		limitParam string
		skip       *int64
		limit      *int64
		bytes      []byte
		logArr     []*common.JobLog
	)

	//解析get参数

	if err = request.ParseForm(); err != nil {
		goto ERR
	}

	jobName = request.Form.Get("jobName")
	skipParam = request.Form.Get("skip")
	limitParam = request.Form.Get("limit")
	//if *limit = int64(strconv.Atoi(limitParam)); err != nil {
	//	*limit = 0
	//}
	if int64Limit, err := strconv.ParseInt(limitParam, 10, 64); err == nil {
		*limit = int64Limit
	}
	if int64Skip, err := strconv.ParseInt(skipParam, 10, 64); err == nil {
		*skip = int64Skip
	}

	if logArr, err = GlobalLogManager.ListLog(jobName, skip, limit); err != nil {
		goto ERR
	}

	if bytes, err = common.BuildResponse(common.RESPONSE_SUCCESS_CODE, common.RESPONSE_SUCCESS_TIPS, logArr); err != nil {
		goto ERR
	}

	writer.Write(bytes)
	//一定记得return，要不然走到error就报错空指针
	return
ERR:
	if bytes, err = common.BuildResponse(common.RESPONSE_ERROR_CODE, err.Error(), nil); err == nil {
		writer.Write(bytes)
	}

}

//获取在线的worker
func handleWorkerList(writer http.ResponseWriter, request *http.Request) {

	var (
		workerArr []string
		err       error
		bytes     []byte
	)
	if workerArr, err = GlobalWorkerManager.ListWorkers(); err != nil {
		goto ERR
	}

	if bytes, err = common.BuildResponse(common.RESPONSE_SUCCESS_CODE, common.RESPONSE_SUCCESS_TIPS, workerArr); err != nil {
		goto ERR
	}

	writer.Write(bytes)
	//一定记得return，要不然走到error就报错空指针
	return
ERR:
	if bytes, err = common.BuildResponse(common.RESPONSE_ERROR_CODE, err.Error(), nil); err == nil {
		writer.Write(bytes)
	}
}

//保存任务接口,保存到etcd，
//postman 接收类型为 x-www-form-urlencoded
// job:{"name":"test7name","command":"echo hello,test3 name","cronExpr":"1/*  *  *  *  *"}↵
//验证结果；root@tuan:~# etcdctl get "/cron/jobs" --prefix
///cron/jobs/test7name
//{"name":"test7name","command":"echo hello,test3 name","cronExpr":"1/*  *  *  *  *"}
//root@tuan:~# etcdctl get "/cron/jobs" --prefix
///cron/jobs/test7name
//{"name":"test7name","command":"echo hello,test3 name","cronExpr":"1/*  *  *  *  *"}
///cron/jobs/test8name
//{"name":"test8name","command":"echo hello,test3 name","cronExpr":"1/*  *  *  *  *"}
func handleJobSave(w http.ResponseWriter, r *http.Request) {

	w.Header().Set("Content-type", "application/text")
	var (
		err      error
		postJob  string
		job      common.Job
		oldJob   *common.Job
		apiBytes []byte
		//apis []byte
		writeRes int
	)

	//1.解析post表单数据
	if err = r.ParseForm(); err != nil {
		goto ERR
	}
	//2接到的是个json字符串，取其中的job字段
	postJob = r.PostForm.Get("job")

	//3.unmarshal参数为[]byte，需要强制转化为字节数组，job为需要保存的struct
	if err = json.Unmarshal([]byte(postJob), &job); err != nil {
		goto ERR
	}
	fmt.Println("Unmarshal 成功")
	fmt.Printf("%v", &job)

	//4. 保存到etcd
	if oldJob, err = GlobalJobManager.SaveJob(&job); err != nil {
		fmt.Println("保存错误")
		goto ERR
	}

	fmt.Println("SaveJob 成功")
	fmt.Println("old job 是", oldJob)
	// 5. 注意这里是没有错误的话，返回正常应答 ({"errno":0,"msg":"",data{...}})
	apiBytes, err = common.BuildResponse(0, "success", oldJob)
	fmt.Println(string(apiBytes), oldJob)
	fmt.Println("err vs nil is :", err == nil)
	if err == nil {

		if writeRes, err = w.Write(apiBytes); err != nil {
			goto ERR
		}
		fmt.Println("写入成功:", writeRes)
	}
	return

ERR:
	fmt.Println("fail err")
	//错误输出错误信息
	//if apiBytes, err = common.BuildResponse(-1, err.Error(), nil); err == nil {
	//	w.Write(apiBytes)
	//}
	if apiBytes, err = json.Marshal("in fail"); err != nil {
		return
	}
	w.Write(apiBytes)
}

func handleJobDelete(w http.ResponseWriter, r *http.Request) {
	var (
		err error
		//需要删除的任务的名字
		DeleteJobName string
		//response数据
		apiBytes []byte
		oldJob   *common.Job
		//删除数量
		delCount int64
	)
	//1.解析post表单数据
	if err = r.ParseForm(); err != nil {
		fmt.Println("parse error")
		goto ERR
	}
	//2接到的是个json字符串，取其中的job字段
	DeleteJobName = r.PostForm.Get("name")
	if oldJob, delCount, err = GlobalJobManager.DelJob(DeleteJobName); err != nil {
		fmt.Println("del error")
		goto ERR
	}
	if apiBytes, err = common.BuildResponse(0, "success,删除条数为 "+strconv.Itoa(int(delCount)), oldJob); err == nil {
		w.Write(apiBytes)
	}
	return

ERR:
	if apiBytes, err = common.BuildResponse(-1, err.Error(), nil); err == nil {
		w.Write(apiBytes)
	}
}

func handleJobView(w http.ResponseWriter, r *http.Request) {

	var (
		bytes []byte
		err   error
	)
	if bytes, err = json.Marshal("hello"); err != nil {
		return
	}
	w.Write(bytes)
}

func handleJobList(resp http.ResponseWriter, req *http.Request) {

	resp.Header().Set("Content-type", "application/json")
	resp.WriteHeader(200)
	var (
		//指针数组
		jobList  []*common.Job
		err      error
		apiBytes []byte
		//writeRes int
		//rlog     common.LogRecord
	)
	if jobList, err = GlobalJobManager.ListJobs(); err != nil {
		goto ERR
	}

	if apiBytes, err = common.BuildResponse(0, "success", jobList); err == nil {
		resp.Write(apiBytes)
	}
	//apiBytes, err = common.BuildResponse(0, "success", jobList)
	//if err == nil {
	//	writeRes, err = resp.Write(apiBytes)
	//	//writeRes, err = rlog.Write(apiBytes)
	//	//w.WriteHeader(200)
	//	fmt.Println("write 成功,字节数为:", writeRes)
	//	fmt.Println("write 成功,值为:", string(apiBytes))
	//}
	return
ERR:
	//错误输出错误信息
	if apiBytes, err = common.BuildResponse(-1, err.Error(), nil); err == nil {
		resp.Write(apiBytes)
	}
}

//export ETCDCTL_API=3 etcdctl watch "/cron/kill/" --prefix
//cron/kill/hello
//2020-04-11 16:54:50
//DELETE
//cron/kill/hello
// 思路是可以让worker watch这个prefix，只关心其put操作。然后利用context将其杀死
func handleJobKill(w http.ResponseWriter, r *http.Request) {
	var (
		err error
		//需要kill的任务的名字
		KillJobName string
		//response数据
		apiBytes  []byte
		killedJob *common.Job
		//接收json value 值， {"job"} {"name":"hello"}
		killJobString string

		//kill的key的值
		killedJobValue string

		//删除数量
	)
	//1.解析post表单数据
	if err = r.ParseForm(); err != nil {
		fmt.Println("parse error")
		goto ERR
	}
	//2接到的是个json字符串，取其中的job字段。当然也可以直接传和直接拿，get name=jobName。这里保留采用value为json，key都为job
	killJobString = r.PostForm.Get("job")
	//3.unmarshal参数为[]byte，需要强制转化为字节数组，job为需要保存的struct
	if err = json.Unmarshal([]byte(killJobString), &killedJob); err != nil {
		fmt.Println(err)
		goto ERR
	}
	KillJobName = killedJob.Name
	if killedJobValue, err = GlobalJobManager.KillJob(KillJobName); err != nil {
		fmt.Println("kill error")
		goto ERR
	}
	if apiBytes, err = common.BuildResponse(0, "success", killedJobValue); err == nil {
		w.Write(apiBytes)
	}
	return

ERR:
	fmt.Println(err)
	if apiBytes, err = common.BuildResponse(-1, err.Error(), nil); err == nil {
		w.Write(apiBytes)
	}
}
